"""
Copyright (c) 2025 Nordic Semiconductor ASA
SPDX-License-Identifier: Apache-2.0
"""

from __future__ import annotations

import argparse
import enum
import os
import pickle
import sys
from pathlib import Path
from typing import Any

try:
    ZEPHYR_BASE = Path(os.environ["ZEPHYR_BASE"]).resolve()
except KeyError:
    sys.exit("Set the environment variable 'ZEPHYR_BASE' to point to the zephyr root directory")

# Add packages that are located in zephyr itself to the python path so we can import them below
sys.path.insert(0, str(ZEPHYR_BASE / "scripts/dts/python-devicetree/src"))
sys.path.insert(0, str(ZEPHYR_BASE / "soc/nordic/common/uicr"))

from periphconf.builder import (
    Ctrlsel,
    FixedPPIMap,
    GpiosProp,
    Node,
    NrfCompChannel,
    NrfFun,
    NrfPsel,
    NrfSaadcChannel,
    PeriphconfBuilder,
    ProcessorId,
    SocLookupTables,
    dt_processor_id,
)

# These peripherals are special cases that don't fit the general rules we use to generate
# PERIPHCONF entries based on the devicetree.
NODELABEL_TO_KWARGS = {
    # Channel links on this node do not result in PPIB connections, because the *-links properties
    # are ambiguous. Instead, the PPIB connections are configured by the DPPICs connected to this.
    "dppic130": {"add_ppib_channel_links": False},
    # The interrupts for this node are not managed in IRQMAP.
    "canpll": {"has_irq_mapping": False},
}


def get_additional_node_kwargs(node: Node) -> dict[str, Any]:
    additional_kwargs = {}
    for label in node.labels:
        additional_kwargs.update(NODELABEL_TO_KWARGS.get(label, {}))
    return additional_kwargs


class Soc(enum.Enum):
    """Names of SoCs supported by this script"""

    NRF54H20 = "nrf54h20"
    NRF9280 = "nrf9280"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        allow_abbrev=False,
        description=(
            "Generate a C source file containing entries for the PERIPHCONF blob based on the "
            "device configuration in devicetree."
        ),
    )
    parser.add_argument(
        "-v",
        "--verbose",
        default=0,
        action="count",
        help="Print verbose output such as debug information.",
    )
    parser.add_argument(
        "--soc",
        required=True,
        choices=[soc.value for soc in Soc],
        help=(
            "SoC to generate PERIPHCONF macros for. "
            "Used to look up soc specific hardware information"
        ),
    )
    parser.add_argument(
        "--in-edt-pickle",
        type=argparse.FileType("rb"),
        required=True,
        help="Path to the pickled edtlib.EDT object with devicetree contents.",
    )
    parser.add_argument(
        "--out-periphconf-source",
        type=argparse.FileType("w", encoding="utf-8"),
        required=True,
        help="Path to write the generated PERIPHCONF C source file to.",
    )
    return parser.parse_args()


def main() -> None:
    args = parse_args()
    dt = pickle.load(args.in_edt_pickle)
    processor = dt_processor_id(dt)
    lookup_tables = lookup_tables_get(Soc(args.soc))
    builder = PeriphconfBuilder(dt, lookup_tables)

    # Application local peripherals
    if processor == ProcessorId.APPLICATION:
        for node in dt.label2node["cpuapp_peripherals"].children.values():
            builder.add_local_peripheral_cfg(node, **get_additional_node_kwargs(node))

    # Radio local peripherals
    if processor == ProcessorId.RADIOCORE:
        for node in dt.label2node["cpurad_peripherals"].children.values():
            builder.add_local_peripheral_cfg(node, **get_additional_node_kwargs(node))

    # Global domain peripherals
    for node in dt.label2node["global_peripherals"].children.values():
        builder.add_global_peripheral_cfg(node, **get_additional_node_kwargs(node))

    # Add pins referenced by 'gpios' properties on non-peripheral nodes, for example
    # buttons and leds. We only add SPU configurations for these and not CTRLSEL,
    # to avoid false CTRLSEL conflicts for things like PWM leds.
    for node in dt.nodes:
        builder.add_gpio_spu_permissions(node)

    script_name = Path(__file__).resolve().relative_to(ZEPHYR_BASE)
    generated_by = f"Generated by {script_name}"

    generated_source = builder.build_generated_source(generated_by)
    args.out_periphconf_source.write(generated_source)


def lookup_tables_get(soc: Soc) -> SocLookupTables:
    if soc == Soc.NRF54H20:
        ctrlsel_lookup = {
            # CAN120
            0x5F8D_8000: {
                # P2
                NrfPsel(fun=NrfFun.CAN_TX, port=2, pin=9): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.CAN_RX, port=2, pin=8): Ctrlsel.CAN_PWM_I3C,
                # P9
                NrfPsel(fun=NrfFun.CAN_TX, port=9, pin=5): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.CAN_RX, port=9, pin=4): Ctrlsel.CAN,
            },
            # PWM120
            0x5F8E_4000: {
                # P2
                NrfPsel(fun=NrfFun.PWM_OUT0, port=2, pin=4): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=2, pin=5): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=2, pin=6): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=2, pin=7): Ctrlsel.CAN_PWM_I3C,
                # P6
                NrfPsel(fun=NrfFun.PWM_OUT0, port=6, pin=6): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=6, pin=7): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=6, pin=8): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=6, pin=9): Ctrlsel.CAN_PWM_I3C,
                # P7
                NrfPsel(fun=NrfFun.PWM_OUT0, port=7, pin=0): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=7, pin=1): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=7, pin=6): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=7, pin=7): Ctrlsel.CAN_PWM_I3C,
            },
            # PWM130
            0x5F9A_4000: {
                # P9
                NrfPsel(fun=NrfFun.PWM_OUT0, port=9, pin=2): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=9, pin=3): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=9, pin=4): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=9, pin=5): Ctrlsel.CAN_PWM_I3C,
            },
            # SPIM130/SPIS130/TWIM130/TWIS130/UARTE130
            0x5F9A_5000: {
                # SPIM mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=9, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=9, pin=2): Ctrlsel.SERIAL0,
                GpiosProp(name="cs-gpios", port=9, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=9, pin=4): Ctrlsel.SERIAL0,
                # SPIS mappings
                NrfPsel(fun=NrfFun.SPIS_MISO, port=9, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_MOSI, port=9, pin=2): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_CSN, port=9, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_SCK, port=9, pin=4): Ctrlsel.SERIAL0,
                # TWIM mappings
                NrfPsel(fun=NrfFun.TWIM_SDA, port=9, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.TWIM_SCL, port=9, pin=4): Ctrlsel.SERIAL0,
                # TWIS mappings
                NrfPsel(fun=NrfFun.TWIS_SDA, port=9, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.TWIS_SCL, port=9, pin=4): Ctrlsel.SERIAL0,
                # UARTÈ mappings
                NrfPsel(fun=NrfFun.UART_TX, port=9, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=9, pin=4): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.UART_CTS, port=9, pin=2): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=9, pin=3): Ctrlsel.SERIAL0,
            },
            # SPIM131/SPIS131/TWIM131/TWIS131/UARTE131
            0x5F9A_6000: {
                # SPIM mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=9, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=9, pin=2): Ctrlsel.CAN_TDM_SERIAL2,
                GpiosProp(name="cs-gpios", port=9, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=9, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                # SPIS mappings
                NrfPsel(fun=NrfFun.SPIS_MISO, port=9, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.SPIS_MOSI, port=9, pin=2): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.SPIS_CSN, port=9, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.SPIS_SCK, port=9, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                # TWIM mappings
                NrfPsel(fun=NrfFun.TWIM_SDA, port=9, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TWIM_SCL, port=9, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                # TWIS mappings
                NrfPsel(fun=NrfFun.TWIS_SDA, port=9, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TWIS_SCL, port=9, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                # UARTÈ mappings
                NrfPsel(fun=NrfFun.UART_TX, port=9, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.UART_RX, port=9, pin=1): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_CTS, port=9, pin=2): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.UART_RTS, port=9, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
            },
            # VPR121 (FLPR)
            0x5F8D_4000: {
                # P1
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=8): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=9): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=10): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=11): Ctrlsel.VPR_GRC,
                # P2
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=0): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=1): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=2): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=3): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=4): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=5): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=6): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=7): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=8): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=9): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=10): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=11): Ctrlsel.VPR_GRC,
                # P6
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=0): Ctrlsel.VPR_GRC,
                # (pin 1-2 are not connected with VIO)
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=3): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=4): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=5): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=6): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=7): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=8): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=9): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=10): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=11): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=12): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=6, pin=13): Ctrlsel.VPR_GRC,
                # P7
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=0): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=1): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=2): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=3): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=4): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=5): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=6): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=7, pin=7): Ctrlsel.VPR_GRC,
                # P9
                NrfPsel(fun=NrfFun.IGNORE, port=9, pin=0): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=9, pin=1): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=9, pin=2): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=9, pin=3): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=9, pin=4): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=9, pin=5): Ctrlsel.VPR_GRC,
            },
            # SPIS120
            0x5F8E_5000: {
                NrfPsel(fun=NrfFun.SPIS_MISO, port=6, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_MOSI, port=6, pin=4): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_CSN, port=6, pin=9): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_SCK, port=6, pin=0): Ctrlsel.SERIAL0,
            },
            # SPIM120/UARTE120
            0x5F8E_6000: {
                # SPIM P6 mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=6, pin=8): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=6, pin=7): Ctrlsel.SERIAL0,
                GpiosProp(name="cs-gpios", port=6, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=6, pin=1): Ctrlsel.SERIAL0,
                # SPIM P7 mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=7, pin=7): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=7, pin=6): Ctrlsel.SERIAL0,
                GpiosProp(name="cs-gpios", port=7, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=7, pin=3): Ctrlsel.SERIAL0,
                # SPIM P2 mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=2, pin=6): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=2, pin=5): Ctrlsel.SERIAL0,
                GpiosProp(name="cs-gpios", port=2, pin=7): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=2, pin=3): Ctrlsel.SERIAL0,
                # UARTÈ P6 mappings
                NrfPsel(fun=NrfFun.UART_TX, port=6, pin=8): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_CTS, port=6, pin=7): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=6, pin=6): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=6, pin=5): Ctrlsel.SERIAL0,
                # UARTÈ P7 mappings
                NrfPsel(fun=NrfFun.UART_TX, port=7, pin=7): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_CTS, port=7, pin=6): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=7, pin=4): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=7, pin=5): Ctrlsel.SERIAL0,
                # UARTÈ P2 mappings
                NrfPsel(fun=NrfFun.UART_TX, port=2, pin=6): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_CTS, port=2, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=2, pin=4): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=2, pin=7): Ctrlsel.SERIAL0,
            },
            # SPIM121
            0x5F8E_7000: {
                # SPIM P6 mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=6, pin=13): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=6, pin=12): Ctrlsel.SERIAL0,
                GpiosProp(name="cs-gpios", port=6, pin=10): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=6, pin=2): Ctrlsel.SERIAL0,
                # SPIM P7 mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=7, pin=1): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.SPIS_MISO, port=7, pin=1): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=7, pin=0): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.SPIS_MOSI, port=7, pin=0): Ctrlsel.EXMIF_RADIO_SERIAL1,
                GpiosProp(name="cs-gpios", port=7, pin=4): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.SPIS_CSN, port=7, pin=4): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=7, pin=2): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.SPIS_SCK, port=7, pin=2): Ctrlsel.EXMIF_RADIO_SERIAL1,
                # SPIM P2 mappings
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=2, pin=11): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=2, pin=10): Ctrlsel.SERIAL0,
                GpiosProp(name="cs-gpios", port=2, pin=8): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=2, pin=2): Ctrlsel.SERIAL0,
            },
            # EXMIF
            0x5F09_5000: {
                NrfPsel(fun=NrfFun.EXMIF_CK, port=6, pin=0): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_RWDS, port=6, pin=2): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_CS0, port=6, pin=3): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ7, port=6, pin=4): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ1, port=6, pin=5): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ6, port=6, pin=6): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ0, port=6, pin=7): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ5, port=6, pin=8): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ3, port=6, pin=9): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ2, port=6, pin=10): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_DQ4, port=6, pin=11): Ctrlsel.EXMIF_RADIO_SERIAL1,
                NrfPsel(fun=NrfFun.EXMIF_CS1, port=6, pin=13): Ctrlsel.EXMIF_RADIO_SERIAL1,
            },
            # VPR130 (PPR)
            0x5F90_8000: {
                # P0
                NrfPsel(fun=NrfFun.IGNORE, port=0, pin=4): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=0, pin=5): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=0, pin=6): Ctrlsel.VPR_GRC,
                NrfPsel(fun=NrfFun.IGNORE, port=0, pin=7): Ctrlsel.VPR_GRC,
            },
            # TDM130
            0x5F99_2000: {
                # TDM P1 mappings
                NrfPsel(fun=NrfFun.TDM_MCK, port=1, pin=2): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_M, port=1, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_S, port=1, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDOUT, port=1, pin=4): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDIN, port=1, pin=5): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_M, port=1, pin=6): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_S, port=1, pin=6): Ctrlsel.CAN_TDM_SERIAL2,
                # TDM P2 mappings
                NrfPsel(fun=NrfFun.TDM_MCK, port=2, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_M, port=2, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_S, port=2, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDOUT, port=2, pin=9): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDIN, port=2, pin=10): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_M, port=2, pin=11): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_S, port=2, pin=11): Ctrlsel.CAN_TDM_SERIAL2,
            },
            # TDM131
            0x5F99_7000: {
                # TDM P1 mappings
                NrfPsel(fun=NrfFun.TDM_MCK, port=1, pin=0): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_M, port=1, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_S, port=1, pin=1): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDOUT, port=1, pin=9): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDIN, port=1, pin=10): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_M, port=1, pin=11): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_S, port=1, pin=11): Ctrlsel.CAN_TDM_SERIAL2,
                # TDM P2 mappings
                NrfPsel(fun=NrfFun.TDM_MCK, port=2, pin=2): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_M, port=2, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SCK_S, port=2, pin=3): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDOUT, port=2, pin=4): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_SDIN, port=2, pin=6): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_M, port=2, pin=7): Ctrlsel.CAN_TDM_SERIAL2,
                NrfPsel(fun=NrfFun.TDM_FSYNC_S, port=2, pin=7): Ctrlsel.CAN_TDM_SERIAL2,
            },
            # GPIOTE0 (RAD)
            0x5302_7000: {
                # P1
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=4): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=5): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=6): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=7): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=8): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=9): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=10): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=1, pin=11): Ctrlsel.CAN,
                # P2
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=0): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=1): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=2): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=3): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=4): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=5): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=6): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=7): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=8): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=9): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=10): Ctrlsel.CAN,
                NrfPsel(fun=NrfFun.IGNORE, port=2, pin=11): Ctrlsel.CAN,
            },
        }
    elif soc == Soc.NRF9280:
        ctrlsel_lookup = {
            # PWM120
            0x5F8E_4000: {
                # P2
                NrfPsel(fun=NrfFun.PWM_OUT0, port=2, pin=0): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=2, pin=1): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=2, pin=2): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=2, pin=3): Ctrlsel.CAN_PWM_I3C,
                # P6
                NrfPsel(fun=NrfFun.PWM_OUT0, port=6, pin=0): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT0, port=6, pin=6): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=6, pin=1): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=6, pin=7): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=6, pin=2): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=6, pin=8): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=6, pin=3): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=6, pin=9): Ctrlsel.CAN_PWM_I3C,
            },
            # SPIM120/UARTE120
            0x5F8E_6000: {
                # SPIM P2 mappings
                GpiosProp(name="cs-gpios", port=2, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=2, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=2, pin=4): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=2, pin=0): Ctrlsel.SERIAL0,
                # SPIM P6 mappings
                GpiosProp(name="cs-gpios", port=6, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=6, pin=7): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=6, pin=8): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=6, pin=1): Ctrlsel.SERIAL0,
                # UARTE P2 mappings
                NrfPsel(fun=NrfFun.UART_CTS, port=2, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=2, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=2, pin=2): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_TX, port=2, pin=4): Ctrlsel.SERIAL0,
                # UARTE P6 mappings
                NrfPsel(fun=NrfFun.UART_CTS, port=6, pin=7): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=6, pin=5): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=6, pin=6): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_TX, port=6, pin=8): Ctrlsel.SERIAL0,
            },
            # SPIM121
            0x5F8E_7000: {
                # P2
                GpiosProp(name="cs-gpios", port=2, pin=6): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=2, pin=8): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=2, pin=9): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=2, pin=1): Ctrlsel.SERIAL0,
                # P6
                GpiosProp(name="cs-gpios", port=6, pin=10): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=6, pin=12): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=6, pin=13): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=6, pin=2): Ctrlsel.SERIAL0,
            },
            # PWM130
            0x5F9A_4000: {
                NrfPsel(fun=NrfFun.PWM_OUT0, port=9, pin=2): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT1, port=9, pin=3): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT2, port=9, pin=4): Ctrlsel.CAN_PWM_I3C,
                NrfPsel(fun=NrfFun.PWM_OUT3, port=9, pin=5): Ctrlsel.CAN_PWM_I3C,
            },
            # SPIM130/SPIS130/TWIM130/TWIS130/UARTE130
            0x5F9A_5000: {
                # SPIM mappings
                GpiosProp(name="cs-gpios", port=9, pin=1): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MISO, port=9, pin=2): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_MOSI, port=9, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIM_SCK, port=9, pin=0): Ctrlsel.SERIAL0,
                # SPIS mappings
                NrfPsel(fun=NrfFun.SPIS_CSN, port=9, pin=1): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_MISO, port=9, pin=3): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_MOSI, port=9, pin=2): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.SPIS_SCK, port=9, pin=0): Ctrlsel.SERIAL0,
                # TWIM mappings
                NrfPsel(fun=NrfFun.TWIM_SCL, port=9, pin=0): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.TWIM_SDA, port=9, pin=3): Ctrlsel.SERIAL0,
                # UARTE mappings
                NrfPsel(fun=NrfFun.UART_CTS, port=9, pin=2): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RTS, port=9, pin=1): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_RX, port=9, pin=4): Ctrlsel.SERIAL0,
                NrfPsel(fun=NrfFun.UART_TX, port=9, pin=3): Ctrlsel.SERIAL0,
            },
        }
    else:
        raise NotImplementedError(f"No CTRLSEL table exists for soc {soc}")

    # Entries below this are common to all supported socs at the time of writing.

    adc_channel_pin_lookup = {
        # SAADC
        0x5F98_2000: {
            NrfSaadcChannel.AIN0: (1, 0),
            NrfSaadcChannel.AIN1: (1, 1),
            NrfSaadcChannel.AIN2: (1, 2),
            NrfSaadcChannel.AIN3: (1, 3),
            NrfSaadcChannel.AIN4: (1, 4),
            NrfSaadcChannel.AIN5: (1, 5),
            NrfSaadcChannel.AIN6: (1, 6),
            NrfSaadcChannel.AIN7: (1, 7),
            NrfSaadcChannel.AIN8: (9, 0),
            NrfSaadcChannel.AIN9: (9, 1),
            NrfSaadcChannel.AIN10: (9, 2),
            NrfSaadcChannel.AIN11: (9, 3),
            NrfSaadcChannel.AIN12: (9, 4),
            NrfSaadcChannel.AIN13: (9, 5),
        }
    }
    comp_channel_pin_lookup = {
        # COMP/LPCOMP
        0x5F98_3000: {
            NrfCompChannel.AIN0: (1, 0),
            NrfCompChannel.AIN1: (1, 1),
            NrfCompChannel.AIN2: (1, 2),
            NrfCompChannel.AIN3: (1, 3),
            NrfCompChannel.AIN4: (1, 4),
            NrfCompChannel.AIN5: (1, 5),
            NrfCompChannel.AIN6: (1, 6),
            NrfCompChannel.AIN7: (1, 7),
            NrfCompChannel.AIN8: (9, 0),
            NrfCompChannel.AIN9: (9, 1),
        }
    }
    spu_instances = [
        ("SPU110", 0x5F08_0000),
        ("SPU111", 0x5F09_0000),
        ("SPU120", 0x5F8C_0000),
        ("SPU121", 0x5F8D_0000),
        ("SPU122", 0x5F8E_0000),
        ("SPU130", 0x5F90_0000),
        ("SPU131", 0x5F92_0000),
        ("SPU132", 0x5F98_0000),
        ("SPU133", 0x5F99_0000),
        ("SPU134", 0x5F9A_0000),
        ("SPU135", 0x5F9B_0000),
        ("SPU136", 0x5F9C_0000),
        ("SPU137", 0x5F9D_0000),
    ]
    dppics = {
        "DPPIC120": 0x5F8E_1000,
        "DPPIC130": 0x5F92_2000,
        "DPPIC131": 0x5F98_1000,
        "DPPIC132": 0x5F99_1000,
        "DPPIC133": 0x5F9A_1000,
        "DPPIC134": 0x5F9B_1000,
        "DPPIC135": 0x5F9C_1000,
        "DPPIC136": 0x5F9D_1000,
    }
    ppib_instances = [
        ("PPIB110", 0x5F09_8000),
        ("PPIB120", 0x5F8E_E000),
        ("PPIB121", 0x5F8E_F000),
        ("PPIB130", 0x5F92_5000),
        ("PPIB131", 0x5F92_6000),
        ("PPIB132", 0x5F98_D000),
        ("PPIB133", 0x5F99_D000),
        ("PPIB134", 0x5F9A_D000),
        ("PPIB135", 0x5F9B_D000),
        ("PPIB136", 0x5F9C_D000),
        ("PPIB137", 0x5F9D_D000),
    ]
    ppib_name_to_addr = dict(ppib_instances)
    dppic_to_ppib_connections = {
        dppics["DPPIC120"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB121"],
            channel_map=range(0, 8),
        ),
        dppics["DPPIC131"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB132"],
            channel_map=range(0, 8),
        ),
        dppics["DPPIC132"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB133"],
            channel_map=range(0, 8),
        ),
        dppics["DPPIC133"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB134"],
            channel_map=range(0, 8),
        ),
        dppics["DPPIC134"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB135"],
            channel_map=range(0, 8),
        ),
        dppics["DPPIC135"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB136"],
            channel_map=range(0, 8),
        ),
        dppics["DPPIC136"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB137"],
            channel_map=range(0, 8),
        ),
    }
    ppib_to_ppib_connections = {
        ppib_name_to_addr["PPIB132"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB130"],
            channel_map=range(0, 8),
        ),
        ppib_name_to_addr["PPIB133"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB130"],
            channel_map=range(8, 16),
        ),
        ppib_name_to_addr["PPIB134"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB130"],
            channel_map=range(16, 24),
        ),
        ppib_name_to_addr["PPIB135"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB130"],
            channel_map=range(24, 32),
        ),
        ppib_name_to_addr["PPIB136"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB131"],
            channel_map=range(0, 8),
        ),
        ppib_name_to_addr["PPIB137"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB131"],
            channel_map=range(8, 16),
        ),
        ppib_name_to_addr["PPIB121"]: FixedPPIMap(
            connected_to=ppib_name_to_addr["PPIB131"],
            channel_map=range(16, 24),
        ),
    }
    return SocLookupTables(
        ctrlsel_lookup=ctrlsel_lookup,
        adc_channel_pin_lookup=adc_channel_pin_lookup,
        comp_channel_pin_lookup=comp_channel_pin_lookup,
        dppic_to_ppib_connections=dppic_to_ppib_connections,
        ppib_to_ppib_connections=ppib_to_ppib_connections,
        spu_instances=spu_instances,
        ppib_instances=ppib_instances,
    )


if __name__ == "__main__":
    main()
